1 Example: Monthly electricity sales for Virginia

1.1 Extract data from remote database

esales <- dbGetQuery(db,'SELECT * from eia_elec_sales_va_all_m') # SQL code to retrieve data from a table in the remote database
# str(esales)
esales <- as_tibble(esales) # Convert dataframe to a 'tibble' for tidyverse work
# str(esales)
# Reference: https://arrow.apache.org/docs/r/
# if(!('arrow' %in% installed.packages())) install.packages('arrow')
library(arrow)
write_feather(esales, "esales.feather")
# Close connection -- this is good practice
dbDisconnect(db)
dbUnloadDriver(db_driver)

1.2 Briefly characterize the dataset

library(arrow)
esales <- read_feather("esales.feather")
print(esales)    # print the data as a table
summary(esales)  # compute basic summary statistics about the data
     value            date                 year          month       
 Min.   : 7153   Min.   :2001-01-01   Min.   :2001   Min.   : 1.000  
 1st Qu.: 8200   1st Qu.:2005-11-01   1st Qu.:2005   1st Qu.: 3.000  
 Median : 9019   Median :2010-09-01   Median :2010   Median : 6.000  
 Mean   : 9093   Mean   :2010-08-31   Mean   :2010   Mean   : 6.425  
 3rd Qu.: 9885   3rd Qu.:2015-07-01   3rd Qu.:2015   3rd Qu.: 9.000  
 Max.   :11724   Max.   :2020-05-01   Max.   :2020   Max.   :12.000  
boxplot(esales)

1.3 Plot the time series

# library(lubridate) # Make it easy to deal with dates

esales %>% filter(month==3)                   # These three lines of code
esales %>% filter(month(date)==3)             #   all do
esales %>% filter(lubridate::month(date)==3)  #   the same thing.

# We don't have to keep the 'year' and 'month' column: can recover them if needed

esales %>%
  select(date, sales_GWh = value) -> esales_tbl

print(esales_tbl)

1.4 Convert the data frame into a time series tsibble object

1.5 Make some plots

1.5.1 Make a histogram of monthly sales

hist(elsales_tbl_ts$sales_GWh, breaks=40)

1.5.2 Make a seasonal plot

# This plot won't work. Why not?
elsales_tbl_ts %>% 
  feasts::gg_season(sales_GWh, labels = "both") + ylab("Virginia electricity sales (GWh)")
autoplot(vaelsales_tbl_ts, sales_GWh) + 
  ylab("Virginia monthly total electricity sales (GWh)") + 
  xlab("")

1.6 Seasonal plots and seasonal subseries plots

vaelsales_tbl_ts %>% gg_season(sales_GWh, labels = "both") + ylab("Virginia electricity sales (GWh)")

vaelsales_tbl_ts %>% 
  gg_subseries(sales_GWh) +
  labs(
    y = "Sales (GWh)",
    title = "Seasonal subseries plot: Virginia electricity sales"
  )

1.7 Scatterplots

Investigating relationships between two variables. Scatterplots. Correlation. Scatterplot matrices.

Readings: FPP Sect. 2.6

vic_elec

summary(vic_elec)
      Time                         Demand      Temperature         Date             Holiday       
 Min.   :2012-01-01 00:00:00   Min.   :2858   Min.   : 1.50   Min.   :2012-01-01   Mode :logical  
 1st Qu.:2012-09-30 22:52:30   1st Qu.:3969   1st Qu.:12.30   1st Qu.:2012-09-30   FALSE:51120    
 Median :2013-07-01 22:45:00   Median :4635   Median :15.40   Median :2013-07-01   TRUE :1488     
 Mean   :2013-07-01 22:45:00   Mean   :4665   Mean   :16.27   Mean   :2013-07-01                  
 3rd Qu.:2014-04-01 23:37:30   3rd Qu.:5244   3rd Qu.:19.40   3rd Qu.:2014-04-01                  
 Max.   :2014-12-31 23:30:00   Max.   :9345   Max.   :43.20   Max.   :2014-12-31                  
vic_elec %>%
  filter(year(Time) == 2013) %>%
  autoplot(Demand) +
  labs(
    y = "Demand (GW)",
    title = "Half-hourly electricity demand: Victoria"
  )

vic_elec %>%
  filter(year(Time) == 2013) %>%
  autoplot(Temperature) +
  labs(
    y = "Temperature (degrees Celsius)",
    title = "Half-hourly temperatures: Melbourne, Australia"
  )

vic_elec %>%
  filter(year(Time) == 2013) %>%
  ggplot(aes(x = Temperature, y = Demand)) + 
#  geom_density2d() +
  geom_point(size=0.1, aes(colour=Holiday), alpha = 0.4) +
  labs(y = "Demand (GW)", x = "Temperature (degrees Celsius)")

A Scatterplot matrix

2 Example: Australian production

# install.packages('tsibbledata')
library(tsibbledata)

aus_production

aus_production %>% gg_season(Electricity)


aus_production %>% gg_season(Beer)

3 Example: Gross Domestic Product data

3.1 Exploratory data analysis

library(tsibbledata) # Data sets package

print(global_economy)
global_economy %>% filter(Country=="Sweden") %>% print()
global_economy %>%
  filter(Country=="Sweden") %>%
  autoplot(GDP) +
  ggtitle("GDP for Sweden") + ylab("$US billions")

3.2 Fitting data to simple models

global_economy %>% model(trend_model = TSLM(GDP ~ trend())) -> fit
7 errors (1 unique) encountered for trend_model
[7] 0 (non-NA) cases
fit
NA
NA
fit %>% filter(Country == "Sweden") %>% residuals()

fit %>% filter(Country == "Sweden") %>% residuals() %>% autoplot(.resid)

3.2.1 Work with ln(GDP)

global_economy %>%
  filter(Country=="Sweden") %>%
  autoplot(log(GDP)) +
  ggtitle("ln(GDP) for Sweden") + ylab("$US billions")

global_economy %>%
  model(trend_model = TSLM(log(GDP) ~ trend())) -> logfit
7 errors (1 unique) encountered for trend_model
[7] 0 (non-NA) cases
logfit %>% filter(Country == "Sweden") %>% residuals() %>% autoplot()
Plot variable not specified, automatically selected `.vars = .resid`

global_economy %>% model(trend_model = TSLM(log(GDP) ~ log(Population))) -> fit3
7 errors (1 unique) encountered for trend_model
[7] 0 (non-NA) cases
fit3 %>% filter(Country == "Sweden") %>% residuals() %>% autoplot()
Plot variable not specified, automatically selected `.vars = .resid`

4 Producing forecasts

fit %>% forecast(h = "3 years") -> fcast3yrs

fcast3yrs
NA

fcast3yrs %>% filter(Country == "Sweden", Year == 2020) %>% str()
fable [1 × 5] (S3: fbl_ts/tbl_ts/tbl_df/tbl/data.frame)
 $ Country: Factor w/ 263 levels "Afghanistan",..: 232
 $ .model : chr "trend_model"
 $ Year   : num 2020
 $ GDP    : dist [1:1] 
  ..$ 3:List of 2
  .. ..$ mu   : num 5.45e+11
  .. ..$ sigma: num 5.34e+10
  .. ..- attr(*, "class")= chr [1:2] "dist_normal" "dist_default"
  ..@ vars: chr "GDP"
 $ .mean  : num 5.45e+11
 - attr(*, "key")= tibble [1 × 3] (S3: tbl_df/tbl/data.frame)
  ..$ Country: Factor w/ 263 levels "Afghanistan",..: 232
  ..$ .model : chr "trend_model"
  ..$ .rows  : list<int> [1:1] 
  .. ..$ : int 1
  .. ..@ ptype: int(0) 
  ..- attr(*, ".drop")= logi TRUE
 - attr(*, "index")= chr "Year"
  ..- attr(*, "ordered")= logi TRUE
 - attr(*, "index2")= chr "Year"
 - attr(*, "interval")= interval [1:1] 1Y
  ..@ .regular: logi TRUE
 - attr(*, "response")= chr "GDP"
 - attr(*, "dist")= chr "GDP"
 - attr(*, "model_cn")= chr ".model"
fcast3yrs %>% 
  filter(Country=="Sweden") %>%
  autoplot(global_economy) +
  ggtitle("GDP for Sweden") + ylab("$US billions")

4.1 Model residuals vs. forecast errors

Model residuals:

Your data: \(y_1, y_2, \ldots, y_T\)

Fitted values: \(\hat{y}_1, \hat{y}_2, \ldots, \hat{y}_T\)

Model residuals: \(e_t = y_t - \hat{y}_t\)

Forecast errors:

augment(fit)
augment(fit) %>% filter(Country == "Sweden") %>%
  ggplot(aes(x = .resid)) +
  geom_histogram(bins = 20) +
  ggtitle("Histogram of residuals")

4.2 Are the model residuals auto-correlated?

augment(fit) %>% filter(Country == "Sweden") -> augSweden

augSweden %>%
  ACF(.resid) %>%
  autoplot() + ggtitle("ACF of residuals")

augment(fit3) %>% filter(Country == "Sweden") -> augSweden3

augSweden3 %>%
  ACF(.resid) %>%
  autoplot() + ggtitle("ACF of residuals")

5 Example: GDP, several countries

library(tsibbledata) # Data sets package

nordic <- c("Sweden", "Denmark", "Norway", "Finland")

(global_economy %>% filter(Country %in% nordic) -> nordic_economy)
NA
nordic_economy %>% autoplot(GDP)

fitnord <- nordic_economy %>%
  model(
    trend_model = TSLM(GDP ~ trend()),
    trend_model_ln = TSLM(log(GDP) ~ trend()),
    ets = ETS(GDP ~ trend("A")),
    arima = ARIMA(GDP)
  )

fitnord
fitnord %>%
  select(arima) %>%
  coef()

Denmark: ARMA(1,1)

Finland: MA(2)

Norway: MA(1)

Sweden: MA(2)

nordic_economy %>%
  model(arima_constrained = ARIMA(GDP ~ pdq(1,0,2))) %>% select(arima_constrained) %>% coef()
It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.4 errors (1 unique) encountered for arima_constrained
[4] Could not find an appropriate ARIMA model.
This is likely because automatic selection does not select models with characteristic roots that may be numerically unstable.
For more details, refer to https://otexts.com/fpp3/arima-r.html#plotting-the-characteristic-roots
fitnord %>% coef() 
fitnord %>%  glance()  
fitnord %>% filter(Country == "Denmark") %>% select(arima) %>% report()
Series: GDP 
Model: ARIMA(1,1,1) 

Coefficients:
          ar1     ma1
      -0.3898  0.7240
s.e.   0.2061  0.1434

sigma^2 estimated as 2.407e+20:  log likelihood=-1417.5
AIC=2840.99   AICc=2841.45   BIC=2847.12
fitnord %>%
  accuracy() %>%
  arrange(Country, MPE)
# ETS forecasts
USAccDeaths %>%
  ets() %>%
  forecast() %>%
  autoplot()
str(taylor)
plot(taylor)

5.1 Plot lagged values

vaelsales_tbl_ts  %>% filter(month(Month) %in% c(3,6,9,12)) %>% gg_lag(sales_GWh, lags = 1:2)

vaelsales_tbl_ts  %>% filter(month(Month) == 1) %>% gg_lag(sales_GWh, lags = 1:2)
vaelsales_tbl_ts %>% ACF(sales_GWh) %>% autoplot()


# decompose(vaelsales_tbl_ts)
vaelsales_tbl_ts %>%
  model(STL(sales_GWh ~ trend(window=21) + season(window='periodic'), robust = TRUE)) %>%
  components() %>%
  autoplot()
vaelsales_tbl_ts %>%
  mutate(ln_sales_GWh = log(sales_GWh)) %>%
  model(STL(ln_sales_GWh ~ trend(window=21) + season(window='periodic'),
    robust = TRUE)) %>%
  components() %>%
  autoplot()
vaelsales_tbl_ts %>%
  features(sales_GWh, feat_stl)
vaelsales_tbl_ts %>%
  features(sales_GWh, feature_set(pkgs="feasts"))

6 References

LS0tCnRpdGxlOiAgICAgIkV4cGxvcmF0b3J5IGFuYWx5c2lzIG9mIHRpbWUgc2VyaWVzIGRhdGE6IEV4YW1wbGVzIgppbnN0aXR1dGU6ICJTWVMgNTU4MSBUaW1lIFNlcmllcyAmIEZvcmVjYXN0aW5nLCBTcHJpbmcgMjAyMSIgCmF1dGhvcjogICAgICJJbnN0cnVjdG9yOiBBcnRodXIgU21hbGwiCmRhdGU6ICAgICAgICJWZXJzaW9uIG9mIGByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGNvZGVfZm9sZGluZzogc2hvdyAjIG9wdGlvbnM6IHNob3csIGhpZGUKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAjIGh0bWxfZG9jdW1lbnQ6CiAgIyAgICAgICBrZWVwX21kOiB5ZXMKICAjIHBkZl9kb2N1bWVudDogZGVmYXVsdApiaWJsaW9ncmFwaHk6IC9Vc2Vycy9BcnRodXIvR2l0UmVwb3MvVGVhY2hpbmcvdGltZS1zZXJpZXMvdHNlcmllcy5iaWIKbGluay1jaXRhdGlvbnM6IHllcwotLS0KCmBgYHtyIHNldCB1cCBjb2RpbmcgZW52aXJvbm1lbnQsIGluY2x1ZGU9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgbGlicmFyeShkcGx5cikgLS0gZG9uJ3QgbmVlZCB0aGlzIGlmIHlvdSBhcmUgbG9hZGluZyB0aGUgZW50aXJlICd0aWR5dmVyc2UnIHN1aXRlCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkgIyBGb3IgZWFzeSBoYW5kbGluZyBvZiB0aW1lLWluZGV4ZWQgb2JqZWN0cwoKIyBpZighKCdmcHAzJyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKSkgaW5zdGFsbC5wYWNrYWdlcygnZnBwMycpCmxpYnJhcnkoZnBwMykKYGBgCgoKIyBFeGFtcGxlOiBNb250aGx5IGVsZWN0cmljaXR5IHNhbGVzIGZvciBWaXJnaW5pYQoKIyMgRXh0cmFjdCBkYXRhIGZyb20gcmVtb3RlIGRhdGFiYXNlCgpgYGB7ciBvcGVuIGNvbm5lY3Rpb24gdG8gZGF0YWJhc2UsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMgT3BlbiBjb25uZWN0aW9uIHRvIGEgcmVtb3RlIGRhdGFiYXNlCiMgTWFrZSBzdXJlIHlvdXIgVlBOIG5ldHdvcmsgY29ubmVjdGlvbiBpcyBhY3RpdmUgaWYgbmVlZGVkIQoKIyBpZighKCdSUG9zdGdyZVNRTCcgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSkpIGluc3RhbGwucGFja2FnZXMoJ1JQb3N0Z3JlU1FMJykKbGlicmFyeShSUG9zdGdyZVNRTCkKCiMgIm15X3Bvc3RncmVzX2NyZWRlbnRpYWxzLlIiIGNvbnRhaW5zIHRoZSBsb2ctaW4gaW5mb3JtYXRpb24Kc291cmNlKCIvVXNlcnMvQXJ0aHVyL0dpdFJlcG9zL1RlYWNoaW5nL215X3Bvc3RncmVzX2RiX2NyZWRlbnRpYWxzLlIiKQoKIyBPcGVuIGNvbm5lY3Rpb24KZGJfZHJpdmVyIDwtIGRiRHJpdmVyKCJQb3N0Z3JlU1FMIikKZGIgPC0gZGJDb25uZWN0KGRiX2RyaXZlcix1c2VyPXVzZXIsIHBhc3N3b3JkPXBhc3N3b3JkLGRibmFtZT0icG9zdGdyZXMiLCBob3N0PWhvc3QpCnJtKHBhc3N3b3JkKSAKCiMgY2hlY2sgdGhlIGNvbm5lY3Rpb246IElmIGZ1bmN0aW9uIHJldHVybnMgdmFsdWUgVFJVRSwgdGhlIGNvbm5lY3Rpb24gaXMgd29ya2luZwpkYkV4aXN0c1RhYmxlKGRiLCAibWV0YWRhdGEiKQpgYGAKCmBgYHtyIHJldHJpZXZlIGRhdGEgZnJvbSBkYiwgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZXNhbGVzIDwtIGRiR2V0UXVlcnkoZGIsJ1NFTEVDVCAqIGZyb20gZWlhX2VsZWNfc2FsZXNfdmFfYWxsX20nKSAjIFNRTCBjb2RlIHRvIHJldHJpZXZlIGRhdGEgZnJvbSBhIHRhYmxlIGluIHRoZSByZW1vdGUgZGF0YWJhc2UKIyBzdHIoZXNhbGVzKQplc2FsZXMgPC0gYXNfdGliYmxlKGVzYWxlcykgIyBDb252ZXJ0IGRhdGFmcmFtZSB0byBhICd0aWJibGUnIGZvciB0aWR5dmVyc2Ugd29yawojIHN0cihlc2FsZXMpCmBgYAoKYGBge3Igc2F2ZSBkYXRhIGluIEFwYWNoZSBBcnJvdyBmb3JtYXQsIGV2YWw9RkFMU0V9CiMgUmVmZXJlbmNlOiBodHRwczovL2Fycm93LmFwYWNoZS5vcmcvZG9jcy9yLwojIGlmKCEoJ2Fycm93JyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKSkgaW5zdGFsbC5wYWNrYWdlcygnYXJyb3cnKQpsaWJyYXJ5KGFycm93KQp3cml0ZV9mZWF0aGVyKGVzYWxlcywgImVzYWxlcy5mZWF0aGVyIikKYGBgCgpgYGB7ciBjbG9zZSBkYiBjb25uZWN0aW9uLCBldmFsPUZBTFNFfQojIENsb3NlIGNvbm5lY3Rpb24gLS0gdGhpcyBpcyBnb29kIHByYWN0aWNlCmRiRGlzY29ubmVjdChkYikKZGJVbmxvYWREcml2ZXIoZGJfZHJpdmVyKQpgYGAKCiMjIEJyaWVmbHkgY2hhcmFjdGVyaXplIHRoZSBkYXRhc2V0CgpgYGB7ciByZWFkIGluIGRhdGF9CmxpYnJhcnkoYXJyb3cpCmVzYWxlcyA8LSByZWFkX2ZlYXRoZXIoImVzYWxlcy5mZWF0aGVyIikKYGBgCgpgYGB7ciB9CnByaW50KGVzYWxlcykgICAgIyBwcmludCB0aGUgZGF0YSBhcyBhIHRhYmxlCnN1bW1hcnkoZXNhbGVzKSAgIyBjb21wdXRlIGJhc2ljIHN1bW1hcnkgc3RhdGlzdGljcyBhYm91dCB0aGUgZGF0YQpib3hwbG90KGVzYWxlcykKYGBgCgpgYGB7ciB1c2UgdGlkeXZlcnNlIHN5bnRheCB0byBwZXJmb3JtIHNvbWUgc2ltcGxlIGRhdGEgbWFuaXB1bGF0aW9uc30KIyBSZWZlcmVuY2VzOiBodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLywgaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnLwojIGZpbHRlcihkYXRhIG9iamVjdCwgY29uZGl0aW9uKSA6IHN5bnRheCBmb3IgZmlsdGVyKCkgY29tbWFuZAoKZXNhbGVzICU+JQogIGZpbHRlcih5ZWFyID09IDIwMTkpICU+JQogIGZpbHRlcih2YWx1ZSA+IDkwMDApICU+JQogIHByaW50KCkKCihlc2FsZXMgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgc3VtbWFyaXNlKFRvdGFsID0gc3VtKHZhbHVlKSkgLT4gdG90YWxfZXNhbGVzX2J5X3llYXIpCgplc2FsZXMgJT4lCiAgbXV0YXRlKHNhbGVzX1RXaCA9IHZhbHVlLzEwMDApICU+JQogIHNlbGVjdCgtdmFsdWUpCmBgYAoKIyMgUGxvdCB0aGUgdGltZSBzZXJpZXMKCmBgYHtyIHVzZSBnZ3Bsb3QyIHRvIGdlbmVyYXRlIGEgcGxvdH0KI1JlZmVyZW5jZTogaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvCgpnZ3Bsb3QoZGF0YT1lc2FsZXMsIGFlcyh4PWRhdGUseT12YWx1ZSkpICsKICBnZW9tX2xpbmUoKSArIHhsYWIoIlllYXIiKSArIHlsYWIoIlZpcmdpbmlhIG1vbnRobHkgdG90YWwgZWxlY3RyaWNpdHkgc2FsZXMgKEdXaCkiKQpgYGAKCmBgYHtyfQojIGxpYnJhcnkobHVicmlkYXRlKSAjIE1ha2UgaXQgZWFzeSB0byBkZWFsIHdpdGggZGF0ZXMKCmVzYWxlcyAlPiUgZmlsdGVyKG1vbnRoPT0zKSAgICAgICAgICAgICAgICAgICAjIFRoZXNlIHRocmVlIGxpbmVzIG9mIGNvZGUKZXNhbGVzICU+JSBmaWx0ZXIobW9udGgoZGF0ZSk9PTMpICAgICAgICAgICAgICMgICBhbGwgZG8KZXNhbGVzICU+JSBmaWx0ZXIobHVicmlkYXRlOjptb250aChkYXRlKT09MykgICMgICB0aGUgc2FtZSB0aGluZy4KCiMgV2UgZG9uJ3QgaGF2ZSB0byBrZWVwIHRoZSAneWVhcicgYW5kICdtb250aCcgY29sdW1uOiBjYW4gcmVjb3ZlciB0aGVtIGlmIG5lZWRlZAoKZXNhbGVzICU+JQogIHNlbGVjdChkYXRlLCBzYWxlc19HV2ggPSB2YWx1ZSkgLT4gZXNhbGVzX3RibAoKcHJpbnQoZXNhbGVzX3RibCkKYGBgCgojIyBDb252ZXJ0IHRoZSBkYXRhIGZyYW1lIGludG8gYSB0aW1lIHNlcmllcyBgdHNpYmJsZWAgb2JqZWN0CgoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygidHNpYmJsZSIpCmxpYnJhcnkodHNpYmJsZSkgIyBSZWZlcmVuY2U6IGh0dHBzOi8vdHNpYmJsZS50aWR5dmVydHMub3JnL2FydGljbGVzL2ludHJvLXRzaWJibGUuaHRtbAoKZXNhbGVzX3RibCAlPiUgYXNfdHNpYmJsZShpbmRleCA9IGRhdGUpIC0+IGVsc2FsZXNfdGJsX3RzCgpwcmludChlbHNhbGVzX3RibF90cykKYGBgCgojIyBNYWtlIHNvbWUgcGxvdHMKCiMjIyAgTWFrZSBhIGhpc3RvZ3JhbSBvZiBtb250aGx5IHNhbGVzCgpgYGB7ciBtYWtlIGEgaGlzdG9ncmFtIG9mIHRoZSBkYXRhfQpoaXN0KGVsc2FsZXNfdGJsX3RzJHNhbGVzX0dXaCwgYnJlYWtzPTQwKQpgYGAKCiMjIyAgTWFrZSBhIHNlYXNvbmFsIHBsb3QKCmBgYHtyLCBldmFsPUZBTFNFfQojIFRoaXMgcGxvdCB3b24ndCB3b3JrLiBXaHkgbm90PwplbHNhbGVzX3RibF90cyAlPiUgCiAgZmVhc3RzOjpnZ19zZWFzb24oc2FsZXNfR1doLCBsYWJlbHMgPSAiYm90aCIpICsgeWxhYigiVmlyZ2luaWEgZWxlY3RyaWNpdHkgc2FsZXMgKEdXaCkiKQpgYGAKCmBgYHtyIENoYW5nZSB0aGUgdHNpYmJsZSBzbyBpbmRleCBpcyBtb250aGx5fQojIGluc3RhbGwucGFja2FnZXMoImZlYXN0cyIpLCBSZWZlcmVuY2U6IGh0dHBzOi8vZmVhc3RzLnRpZHl2ZXJ0cy5vcmcvCmxpYnJhcnkoZmVhc3RzKQoKZWxzYWxlc190YmxfdHMgJT4lIAogIG11dGF0ZShNb250aCA9IHRzaWJibGU6OnllYXJtb250aChkYXRlKSkgJT4lIAogIGFzX3RzaWJibGUoaW5kZXggPSBNb250aCkgJT4lIAogIHNlbGVjdChNb250aCxzYWxlc19HV2gpIC0+IHZhZWxzYWxlc190YmxfdHMKCnByaW50KHZhZWxzYWxlc190YmxfdHMpCmBgYApgYGB7cn0KYXV0b3Bsb3QodmFlbHNhbGVzX3RibF90cywgc2FsZXNfR1doKSArIAogIHlsYWIoIlZpcmdpbmlhIG1vbnRobHkgdG90YWwgZWxlY3RyaWNpdHkgc2FsZXMgKEdXaCkiKSArIAogIHhsYWIoIiIpICAjIExlYXZlIGhvcml6LiBheGlzIGxhYmVsIGJsYW5rCmBgYAojIyBTZWFzb25hbCBwbG90cyBhbmQgc2Vhc29uYWwgc3Vic2VyaWVzIHBsb3RzCgpgYGB7ciAsIHdhcm5pbmc9RkFMU0V9CnZhZWxzYWxlc190YmxfdHMgJT4lIGdnX3NlYXNvbihzYWxlc19HV2gsIGxhYmVscyA9ICJib3RoIikgKyB5bGFiKCJWaXJnaW5pYSBlbGVjdHJpY2l0eSBzYWxlcyAoR1doKSIpCmBgYAoKCmBgYHtyfQp2YWVsc2FsZXNfdGJsX3RzICU+JSAKICBnZ19zdWJzZXJpZXMoc2FsZXNfR1doKSArCiAgbGFicygKICAgIHkgPSAiU2FsZXMgKEdXaCkiLAogICAgdGl0bGUgPSAiU2Vhc29uYWwgc3Vic2VyaWVzIHBsb3Q6IFZpcmdpbmlhIGVsZWN0cmljaXR5IHNhbGVzIgogICkKYGBgCgojIyBTY2F0dGVycGxvdHMKCkludmVzdGlnYXRpbmcgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHR3byB2YXJpYWJsZXMuIFNjYXR0ZXJwbG90cy4gQ29ycmVsYXRpb24uIFNjYXR0ZXJwbG90IG1hdHJpY2VzLgoKUmVhZGluZ3M6IEZQUCBTZWN0LiAyLjYKCmBgYHtyfQp2aWNfZWxlYwoKc3VtbWFyeSh2aWNfZWxlYykKCnZpY19lbGVjICU+JQogIGZpbHRlcih5ZWFyKFRpbWUpID09IDIwMTMpICU+JQogIGF1dG9wbG90KERlbWFuZCkgKwogIGxhYnMoCiAgICB5ID0gIkRlbWFuZCAoR1cpIiwKICAgIHRpdGxlID0gIkhhbGYtaG91cmx5IGVsZWN0cmljaXR5IGRlbWFuZDogVmljdG9yaWEiCiAgKQpgYGAKYGBge3J9CnZpY19lbGVjICU+JQogIGZpbHRlcih5ZWFyKFRpbWUpID09IDIwMTMpICU+JQogIGF1dG9wbG90KFRlbXBlcmF0dXJlKSArCiAgbGFicygKICAgIHkgPSAiVGVtcGVyYXR1cmUgKGRlZ3JlZXMgQ2Vsc2l1cykiLAogICAgdGl0bGUgPSAiSGFsZi1ob3VybHkgdGVtcGVyYXR1cmVzOiBNZWxib3VybmUsIEF1c3RyYWxpYSIKICApCmBgYAoKYGBge3J9CnZpY19lbGVjICU+JQogIGZpbHRlcih5ZWFyKFRpbWUpID09IDIwMTMpICU+JQogIGdncGxvdChhZXMoeCA9IFRlbXBlcmF0dXJlLCB5ID0gRGVtYW5kKSkgKyAKIyAgZ2VvbV9kZW5zaXR5MmQoKSArCiAgZ2VvbV9wb2ludChzaXplPTAuMSwgYWVzKGNvbG91cj1Ib2xpZGF5KSwgYWxwaGEgPSAwLjQpICsKICBsYWJzKHkgPSAiRGVtYW5kIChHVykiLCB4ID0gIlRlbXBlcmF0dXJlIChkZWdyZWVzIENlbHNpdXMpIikKYGBgCkEgU2NhdHRlcnBsb3QgbWF0cml4CgpgYGB7ciwgd2FybmluZz1GQUxTRX0KdmljX2VsZWMKCmJveHBsb3QodmljX2VsZWMkVGVtcGVyYXR1cmUpCgojIGluc3RhbGwucGFja2FnZXMoIkdHYWxseSIpCnZpY19lbGVjICU+JSAKICAjIG11dGF0ZShUZW1wZXJhdHVyZSA9IHJvdW5kKFRlbXBlcmF0dXJlKSkgJT4lCiAgIyBwaXZvdF93aWRlcih2YWx1ZXNfZnJvbT1jKERlbWFuZCxUZW1wZXJhdHVyZSksIG5hbWVzX2Zyb209SG9saWRheSkgJT4lCiAgR0dhbGx5OjpnZ3BhaXJzKGNvbHVtbnMgPSAzOjIpCgp2aWNfZWxlYyAlPiUgCiAgbXV0YXRlKFllYXIgPSBmYWN0b3IoeWVhcihEYXRlKSkpICU+JQogIHNlbGVjdCgtYyhEYXRlLCBIb2xpZGF5KSkgJT4lCiAgR0dhbGx5OjpnZ3BhaXJzKGNvbHVtbnMgPSA0OjIpCmBgYAoKIyBFeGFtcGxlOiBBdXN0cmFsaWFuIHByb2R1Y3Rpb24KCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQojIGluc3RhbGwucGFja2FnZXMoJ3RzaWJibGVkYXRhJykKbGlicmFyeSh0c2liYmxlZGF0YSkKCmF1c19wcm9kdWN0aW9uCgphdXNfcHJvZHVjdGlvbiAlPiUgZ2dfc2Vhc29uKEVsZWN0cmljaXR5KQoKYXVzX3Byb2R1Y3Rpb24gJT4lIGdnX3NlYXNvbihCZWVyKQpgYGAKCiMgRXhhbXBsZTogR3Jvc3MgRG9tZXN0aWMgUHJvZHVjdCBkYXRhCgojIyBFeHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzCgpgYGB7cn0KbGlicmFyeSh0c2liYmxlZGF0YSkgIyBEYXRhIHNldHMgcGFja2FnZQoKcHJpbnQoZ2xvYmFsX2Vjb25vbXkpCmBgYAoKYGBge3J9Cmdsb2JhbF9lY29ub215ICU+JSBmaWx0ZXIoQ291bnRyeT09IlN3ZWRlbiIpICU+JSBwcmludCgpCmBgYAoKYGBge3J9Cmdsb2JhbF9lY29ub215ICU+JQogIGZpbHRlcihDb3VudHJ5PT0iU3dlZGVuIikgJT4lCiAgYXV0b3Bsb3QoR0RQKSArCiAgZ2d0aXRsZSgiR0RQIGZvciBTd2VkZW4iKSArIHlsYWIoIiRVUyBiaWxsaW9ucyIpCmBgYAoKIyMgRml0dGluZyBkYXRhIHRvIHNpbXBsZSBtb2RlbHMKCmBgYHtyfQpnbG9iYWxfZWNvbm9teSAlPiUgbW9kZWwodHJlbmRfbW9kZWwgPSBUU0xNKEdEUCB+IHRyZW5kKCkpKSAtPiBmaXQKCmZpdAoKCmBgYAoKYGBge3J9CmZpdCAlPiUgZmlsdGVyKENvdW50cnkgPT0gIlN3ZWRlbiIpICU+JSByZXNpZHVhbHMoKQpgYGAKCmBgYHtyfQoKZml0ICU+JSBmaWx0ZXIoQ291bnRyeSA9PSAiU3dlZGVuIikgJT4lIHJlc2lkdWFscygpICU+JSBhdXRvcGxvdCgucmVzaWQpCmBgYAoKIyMjIFdvcmsgd2l0aCBsbihHRFApCgpgYGB7cn0KZ2xvYmFsX2Vjb25vbXkgJT4lCiAgZmlsdGVyKENvdW50cnk9PSJTd2VkZW4iKSAlPiUKICBhdXRvcGxvdChsb2coR0RQKSkgKwogIGdndGl0bGUoImxuKEdEUCkgZm9yIFN3ZWRlbiIpICsgeWxhYigiJFVTIGJpbGxpb25zIikKYGBgCgpgYGB7cn0KZ2xvYmFsX2Vjb25vbXkgJT4lCiAgbW9kZWwodHJlbmRfbW9kZWwgPSBUU0xNKGxvZyhHRFApIH4gdHJlbmQoKSkpIC0+IGxvZ2ZpdApgYGAKCmBgYHtyfQpsb2dmaXQgJT4lIGZpbHRlcihDb3VudHJ5ID09ICJTd2VkZW4iKSAlPiUgcmVzaWR1YWxzKCkgJT4lIGF1dG9wbG90KCkKYGBgCgpgYGB7cn0KZ2xvYmFsX2Vjb25vbXkgJT4lIG1vZGVsKHRyZW5kX21vZGVsID0gVFNMTShsb2coR0RQKSB+IGxvZyhQb3B1bGF0aW9uKSkpIC0+IGZpdDMKCmZpdDMgJT4lIGZpbHRlcihDb3VudHJ5ID09ICJTd2VkZW4iKSAlPiUgcmVzaWR1YWxzKCkgJT4lIGF1dG9wbG90KCkKCmBgYAoKIyBQcm9kdWNpbmcgZm9yZWNhc3RzCgpgYGB7cn0KZml0ICU+JSBmb3JlY2FzdChoID0gIjMgeWVhcnMiKSAtPiBmY2FzdDN5cnMKCmZjYXN0M3lycwoKYGBgCgpgYGB7cn0KCmZjYXN0M3lycyAlPiUgZmlsdGVyKENvdW50cnkgPT0gIlN3ZWRlbiIsIFllYXIgPT0gMjAyMCkgJT4lIHN0cigpCmBgYAoKYGBge3IgdmlzdWFsaXplIGZvcmVjYXN0c30KZmNhc3QzeXJzICU+JSAKICBmaWx0ZXIoQ291bnRyeT09IlN3ZWRlbiIpICU+JQogIGF1dG9wbG90KGdsb2JhbF9lY29ub215KSArCiAgZ2d0aXRsZSgiR0RQIGZvciBTd2VkZW4iKSArIHlsYWIoIiRVUyBiaWxsaW9ucyIpCmBgYAoKIyMgTW9kZWwgcmVzaWR1YWxzIHZzLiBmb3JlY2FzdCBlcnJvcnMKCk1vZGVsIHJlc2lkdWFsczoKCllvdXIgZGF0YTogJHlfMSwgeV8yLCBcbGRvdHMsIHlfVCQKCkZpdHRlZCB2YWx1ZXM6ICRcaGF0e3l9XzEsIFxoYXR7eX1fMiwgXGxkb3RzLCBcaGF0e3l9X1QkCgpNb2RlbCByZXNpZHVhbHM6ICRlX3QgPSB5X3QgLSBcaGF0e3l9X3QkCgpGb3JlY2FzdCBlcnJvcnM6CgpgYGB7cn0KYXVnbWVudChmaXQpCmBgYAoKYGBge3J9CmF1Z21lbnQoZml0KSAlPiUgZmlsdGVyKENvdW50cnkgPT0gIlN3ZWRlbiIpICU+JQogIGdncGxvdChhZXMoeCA9IC5yZXNpZCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjApICsKICBnZ3RpdGxlKCJIaXN0b2dyYW0gb2YgcmVzaWR1YWxzIikKYGBgCgojIyBBcmUgdGhlIG1vZGVsIHJlc2lkdWFscyBhdXRvLWNvcnJlbGF0ZWQ/CgpgYGB7cn0KYXVnbWVudChmaXQpICU+JSBmaWx0ZXIoQ291bnRyeSA9PSAiU3dlZGVuIikgLT4gYXVnU3dlZGVuCgphdWdTd2VkZW4gJT4lCiAgQUNGKC5yZXNpZCkgJT4lCiAgYXV0b3Bsb3QoKSArIGdndGl0bGUoIkFDRiBvZiByZXNpZHVhbHMiKQpgYGAKCmBgYHtyfQphdWdtZW50KGZpdDMpICU+JSBmaWx0ZXIoQ291bnRyeSA9PSAiU3dlZGVuIikgLT4gYXVnU3dlZGVuMwoKYXVnU3dlZGVuMyAlPiUKICBBQ0YoLnJlc2lkKSAlPiUKICBhdXRvcGxvdCgpICsgZ2d0aXRsZSgiQUNGIG9mIHJlc2lkdWFscyIpCmBgYAoKCiMgRXhhbXBsZTogR0RQLCBzZXZlcmFsIGNvdW50cmllcwoKCmBgYHtyfQpsaWJyYXJ5KHRzaWJibGVkYXRhKSAjIERhdGEgc2V0cyBwYWNrYWdlCgpub3JkaWMgPC0gYygiU3dlZGVuIiwgIkRlbm1hcmsiLCAiTm9yd2F5IiwgIkZpbmxhbmQiKQoKKGdsb2JhbF9lY29ub215ICU+JSBmaWx0ZXIoQ291bnRyeSAlaW4lIG5vcmRpYykgLT4gbm9yZGljX2Vjb25vbXkpCgpgYGAKCmBgYHtyfQpub3JkaWNfZWNvbm9teSAlPiUgYXV0b3Bsb3QoR0RQKQpgYGAKCmBgYHtyfQpmaXRub3JkIDwtIG5vcmRpY19lY29ub215ICU+JQogIG1vZGVsKAogICAgdHJlbmRfbW9kZWwgPSBUU0xNKEdEUCB+IHRyZW5kKCkpLAogICAgdHJlbmRfbW9kZWxfbG4gPSBUU0xNKGxvZyhHRFApIH4gdHJlbmQoKSksCiAgICBldHMgPSBFVFMoR0RQIH4gdHJlbmQoIkEiKSksCiAgICBhcmltYSA9IEFSSU1BKEdEUCkKICApCgpmaXRub3JkCmBgYAoKYGBge3J9CmZpdG5vcmQgJT4lCiAgc2VsZWN0KGFyaW1hKSAlPiUKICBjb2VmKCkKYGBgCgoKRGVubWFyazogQVJNQSgxLDEpCgpGaW5sYW5kOiBNQSgyKQoKTm9yd2F5OiBNQSgxKQoKU3dlZGVuOiBNQSgyKQoKYGBge3J9Cm5vcmRpY19lY29ub215ICU+JQogIG1vZGVsKGFyaW1hX2NvbnN0cmFpbmVkID0gQVJJTUEoR0RQIH4gcGRxKDEsMCwyKSkpICU+JSBzZWxlY3QoYXJpbWFfY29uc3RyYWluZWQpICU+JSBjb2VmKCkKYGBgCgpgYGB7cn0KZml0bm9yZCAlPiUgY29lZigpIApgYGAKCmBgYHtyfQpmaXRub3JkICU+JSAgZ2xhbmNlKCkgIApgYGAKCmBgYHtyfQpmaXRub3JkICU+JSBmaWx0ZXIoQ291bnRyeSA9PSAiRGVubWFyayIpICU+JSBzZWxlY3QoYXJpbWEpICU+JSByZXBvcnQoKQpgYGAKCmBgYHtyfQpmaXRub3JkICU+JQogIGFjY3VyYWN5KCkgJT4lCiAgYXJyYW5nZShDb3VudHJ5LCBNUEUpCmBgYAoKCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBFVFMgZm9yZWNhc3RzClVTQWNjRGVhdGhzICU+JQogIGV0cygpICU+JQogIGZvcmVjYXN0KCkgJT4lCiAgYXV0b3Bsb3QoKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpzdHIodGF5bG9yKQpwbG90KHRheWxvcikKYGBgCgojIyBQbG90IGxhZ2dlZCB2YWx1ZXMKCmBgYHtyIHBsb3QgbGFnZ2VkIHZhbHVlc30KdmFlbHNhbGVzX3RibF90cyAgJT4lIGZpbHRlcihtb250aChNb250aCkgJWluJSBjKDMsNiw5LDEyKSkgJT4lIGdnX2xhZyhzYWxlc19HV2gsIGxhZ3MgPSAxOjIpCgp2YWVsc2FsZXNfdGJsX3RzICAlPiUgZmlsdGVyKG1vbnRoKE1vbnRoKSA9PSAxKSAlPiUgZ2dfbGFnKHNhbGVzX0dXaCwgbGFncyA9IDE6MikKYGBgCgpgYGB7cn0KdmFlbHNhbGVzX3RibF90cyAlPiUgQUNGKHNhbGVzX0dXaCkgJT4lIGF1dG9wbG90KCkKYGBgCgpgYGB7ciBwZXJmb3JtIGF1dG9tYXRlZCB0aW1lIHNlcmllcyBkZWNvbXBvc2l0aW9ufQoKCiMgZGVjb21wb3NlKHZhZWxzYWxlc190YmxfdHMpCmBgYAoKYGBge3IgcGVyZm9ybSBhZGRpdGl2ZSBTVEwgZGVjb21wb3NpdGlvbiBvZiB0aGUgVkEgZWxlY3RyaWNpdHkgc2FsZXMgdGltZSBzZXJpZXN9CnZhZWxzYWxlc190YmxfdHMgJT4lCiAgbW9kZWwoU1RMKHNhbGVzX0dXaCB+IHRyZW5kKHdpbmRvdz0yMSkgKyBzZWFzb24od2luZG93PSdwZXJpb2RpYycpLCByb2J1c3QgPSBUUlVFKSkgJT4lCiAgY29tcG9uZW50cygpICU+JQogIGF1dG9wbG90KCkKYGBgCgpgYGB7ciBwZXJmb3JtIG11bHRpcGxpY2F0aXZlIFNUTCBkZWNvbXBvc2l0aW9uIG9mIHRoZSBWQSBlbGVjdHJpY2l0eSBzYWxlcyB0aW1lIHNlcmllc30KdmFlbHNhbGVzX3RibF90cyAlPiUKICBtdXRhdGUobG5fc2FsZXNfR1doID0gbG9nKHNhbGVzX0dXaCkpICU+JQogIG1vZGVsKFNUTChsbl9zYWxlc19HV2ggfiB0cmVuZCh3aW5kb3c9MjEpICsgc2Vhc29uKHdpbmRvdz0ncGVyaW9kaWMnKSwKICAgIHJvYnVzdCA9IFRSVUUpKSAlPiUKICBjb21wb25lbnRzKCkgJT4lCiAgYXV0b3Bsb3QoKQpgYGAKCmBgYHtyfQp2YWVsc2FsZXNfdGJsX3RzICU+JQogIGZlYXR1cmVzKHNhbGVzX0dXaCwgZmVhdF9zdGwpCmBgYAoKYGBge3J9CnZhZWxzYWxlc190YmxfdHMgJT4lCiAgZmVhdHVyZXMoc2FsZXNfR1doLCBmZWF0dXJlX3NldChwa2dzPSJmZWFzdHMiKSkKYGBgCgoKCiMgUmVmZXJlbmNlcwo=